<script setup lang="ts">
import { ref } from 'vue';
import { fs, path, childProcess } from '../nodeTools';
import TitleView from './views/TitleView.vue';
interface GeoPoint {
  x: number;
  y: number;
}
interface Point extends GeoPoint {
  label: number;
  acceptable: number[];
}
/**
 * @brief https://www.yisu.com/zixun/171263.html
 */
const gaussBlur = (imgData: ImageData) => {
  let pixes = imgData.data;
  let width = imgData.width;
  let height = imgData.height;
  let gaussMatrix = [],
    gaussSum = 0,
    x, y,
    r, g, b, a,
    i, j, k, len;

  let radius = 20;
  let sigma = 10;

  a = 1 / (Math.sqrt(2 * Math.PI) * sigma);
  b = -1 / (2 * sigma * sigma);
  //生成高斯矩阵
  for (i = 0, x = -radius; x <= radius; x++, i++) {
    g = a * Math.exp(b * x * x);
    gaussMatrix[i] = g;
    gaussSum += g;

  }

  //归一化, 保证高斯矩阵的值在[0,1]之间
  for (i = 0, len = gaussMatrix.length; i < len; i++) {
    gaussMatrix[i] /= gaussSum;
  }
  //x 方向一维高斯运算
  for (y = 0; y < height; y++) {
    for (x = 0; x < width; x++) {
      r = g = b = a = 0;
      gaussSum = 0;
      for (j = -radius; j <= radius; j++) {
        k = x + j;
        if (k >= 0 && k < width) {//确保 k 没超出 x 的范围
          //r,g,b,a 四个一组
          i = (y * width + k) * 4;
          r += pixes[i] * gaussMatrix[j + radius];
          g += pixes[i + 1] * gaussMatrix[j + radius];
          b += pixes[i + 2] * gaussMatrix[j + radius];
          // a += pixes[i + 3] * gaussMatrix[j];
          gaussSum += gaussMatrix[j + radius];
        }
      }
      i = (y * width + x) * 4;
      // 除以 gaussSum 是为了消除处于边缘的像素, 高斯运算不足的问题
      // console.log(gaussSum)
      pixes[i] = r / gaussSum;
      pixes[i + 1] = g / gaussSum;
      pixes[i + 2] = b / gaussSum;
      // pixes[i + 3] = a ;
    }
  }
  //y 方向一维高斯运算
  for (x = 0; x < width; x++) {
    for (y = 0; y < height; y++) {
      r = g = b = a = 0;
      gaussSum = 0;
      for (j = -radius; j <= radius; j++) {
        k = y + j;
        if (k >= 0 && k < height) {//确保 k 没超出 y 的范围
          i = (k * width + x) * 4;
          r += pixes[i] * gaussMatrix[j + radius];
          g += pixes[i + 1] * gaussMatrix[j + radius];
          b += pixes[i + 2] * gaussMatrix[j + radius];
          // a += pixes[i + 3] * gaussMatrix[j];
          gaussSum += gaussMatrix[j + radius];
        }
      }
      i = (y * width + x) * 4;
      pixes[i] = r / gaussSum;
      pixes[i + 1] = g / gaussSum;
      pixes[i + 2] = b / gaussSum;
    }
  }
  return imgData;
};
const paint = (data: Point[]) => {
  const canvas = document.querySelector<HTMLCanvasElement>('canvas#canvas');
  if (canvas === null) {
    throw new Error('找不到canvas');
  }
  const context = canvas.getContext('2d');
  if (!context) {
    throw new Error('Cannot get context 2d.');
  }
  const width = canvas.width;
  const height = canvas.height;
  context.clearRect(0, 0, width, height);
  context.fillStyle = 'white';
  context.getImageData;
  context.fillRect(0, 0, width, height);
  if (data.length === 0) {
    return;
  }
  const piece = 59;
  for (const point of data) {
    if (point.label !== -1) {
      continue;
    }
    const { x, y } = point;
    context.save();
    const rgb = point.acceptable.map(d => Math.min(255, (Math.floor(d * 100) + 155)));
    context.fillStyle = `rgba(${rgb.join(',')})`;
    context.fillRect(x * width - width / piece / 2, y * height - height / piece / 2, width / piece + 1, height / piece + 1);
    context.restore();
  }
  context.putImageData(gaussBlur(context.getImageData(0, 0, width, height)), 0, 0);
  for (const point of data) {
    if (point.label === -1) {
      continue;
    }
    context.save();
    context.fillStyle = ['red', 'green', 'blue'][point.label];
    context.beginPath();
    context.arc(point.x * width, point.y * height, 5, 0, 2 * Math.PI);
    context.fill();
    context.restore();
  }
};
const toPoint = (v: string) => {
  const list = v.trim().split(/\s+/).map(d => +d);
  const [x, y, label, ...acceptable] = list;
  return { x, y, label, acceptable };
};
const getJarPath = () => {
  const paths = ['./'];
  const jarName = 'JavaMLTest.jar';
  while (paths.length < 5 && !fs.readdirSync(path.resolve(...paths)).includes(jarName)) {
    paths.push('../');
  }
  if (paths.length >= 5) {
    const errorMessage = 'Cannot find jar file.';
    alert(errorMessage);
    throw new Error(errorMessage);
  }
  paths.push(jarName);
  return path.resolve(...paths);
};
const jarPath = getJarPath();
const runningJar = ref('');
const callJar = () => {
  if (runningJar.value.startsWith('正在运行')) {
    alert('正在运行，请勿重复调用');
    return;
  }
  const fileSelector = document.querySelector<HTMLInputElement>('input#fileSelector');
  if (fileSelector === null) {
    throw new Error('找不到文件选择器');
  }
  const jarDir = path.dirname(jarPath);
  const file = fileSelector.files?.item(0);
  const csvName = file ? file.name : 'SIMULATED_00001.csv';
  const csvPath = path.resolve(jarDir, 'resource/data', csvName);
  if (!fs.existsSync(csvPath)) {
    const errorMessage = '没有在resource/data中发现此csv';
    alert(errorMessage);
    throw new Error(errorMessage);
  }
  const logPath = path.resolve(jarDir, 'output-log.txt');
  const cmd = `cd "${jarDir}" && java -jar "${jarPath}" "${csvPath}" > "${logPath}"`;
  runningJar.value = '正在运行...';
  const filePath = path.resolve(jarDir, 'points.javaml-point.txt');
  if (fs.existsSync(filePath)) {
    fs.rmSync(filePath);
  }
  childProcess.exec(cmd, () => {
    runningJar.value = '执行完毕';
    if (!fs.existsSync(filePath)) {
      const errorMessage = '执行失败或读取点阵失败';
      alert(errorMessage);
      runningJar.value = '';
      throw new Error(errorMessage);
    }
    const fileContent = fs.readFileSync(filePath).toString();
    const lines = fileContent.split('\n').map(line => line.trim()).filter(line => line !== '');
    const points = lines.map(line => toPoint(line));
    paint(points);
  });
};
</script>

<template>
  <header>
    <TitleView :title="'JavaML-Electron'" />
    <div class="wrapper">
      <div>找到JavaML项目的Jar包于：{{jarPath}}</div>
      <div>
        <input id="fileSelector" type="file" accept=".csv" />
      </div>
      <div>
        <div class="normal-button" @click="callJar">执行jar</div>
        <div :style="{display:'inline-block'}" v-if="runningJar!==''">{{runningJar}}</div>
      </div>
      <div :style="{display:(runningJar==='执行完毕'?'block':'none')}">
        <canvas id="canvas" width="600" height="400"></canvas>
      </div>
    </div>
  </header>
</template>

<style scoped>
.wrapper {
  max-width: 100%;
  text-align: center;
  overflow-wrap: break-word;
}

.normal-button {
  background-color: rgb(230, 230, 230);
  padding: 10px;
  border-radius: 5px;
  cursor: pointer;
  display: inline-block;
  margin: 5px;
}

.normal-button:hover {
  background-color: rgb(210, 210, 210);
}
</style>
